﻿// dataBox options: {data, selectedIndex, selectedClass, selectedChanged(selectedIndex), checkedIndexes, checkedClass, checkedChanged(checkedIndexes)}
// {'getSelectedIndex'}, {'getCheckedIndexes'}, {'getData'}
// {'checkAll'}, {'uncheckAll'}
// {'append', {data(one/array)}}, {'insert', {index, data(one/array)}}, {'delete', index(es)}, {'update', {index, data(one)}/[{index, data(one)}, {index, data(one)}...]}
+function ($) {
    "use strict";

    // Class
    var DataBox = function (container) {
        this.$container = $(container);
        this.options = $.extend({}, DataBox.DEFAULTS);
    };

    DataBox.VERSION = '1.0.1';

    DataBox.DEFAULTS = {
        selectedClass: "info",
        selectedChanged: undefined,
        checkedClass: "active",
        checkedChanged: undefined,
    };

    DataBox.prototype.setting = function (opts, ignoreOnChanged) {
        var $container = this.$container;
        this.options = $.extend(this.options, opts);
        delete this.options.data;
        delete this.options.selectedIndex;
        delete this.options.checkedIndexes;

        var selectedClass = this.options.selectedClass;
        var checkedClass = this.options.checkedClass;
        var selectedChanged = this.options.selectedChanged;
        var checkedChanged = this.options.checkedChanged;

        var instance = this;
        var onSelectedChanged = function (index) {
            var sel = instance.getSelectedIndex();
            if (index != sel) {
                if (selectedChanged != null) {
                    selectedChanged(sel);
                }
            }
        };

        var onCheckedChanged = function (indexes) {
            var chk = instance.getCheckedIndexes();
            if (chk.toString() != indexes.toString()) {
                if (checkedChanged != null) {
                    checkedChanged(chk);
                }
            }
        };

        var data = opts.data;
        var selectedIndex = opts.selectedIndex;
        var checkedIndexes = opts.checkedIndexes;

        if (data != null) {
            $.xfont._render($container, data);

            // rowHeader-checkbox
            $container.children().find('[data-rowHeader] input[type=checkbox]').click(function (event) {
                var old_sel = instance.getSelectedIndex();

                if ($(this).prop('checked')) {
                    $(this).parent().parent().addClass(checkedClass);
                }
                else {
                    $(this).parent().parent().removeClass(checkedClass).removeClass(selectedClass);
                }

                onSelectedChanged(old_sel);
                onCheckedChanged([-1]);
            });

            // items
            $container.children().each(function (index, element) {
                $(element).attr('data-data', JSON.stringify(data[index]));

                // item selected
                $(element).children('[data-rowHeader!=""]').mousedown(function (event) {
                    var old_sel = instance.getSelectedIndex();
                    var old_chk = instance.getCheckedIndexes();

                    $container.children().removeClass(checkedClass).removeClass(selectedClass);
                    $(this).parent().addClass(selectedClass);
                    $container.children().find('[data-rowHeader] input[type=checkbox]').prop('checked', false);
                    $(this).parent().find('[data-rowHeader] input[type=checkbox]').prop('checked', true);

                    onSelectedChanged(old_sel);
                    onCheckedChanged(old_chk);
                });
            });

            if (selectedIndex == null) selectedIndex = -1;
            if (checkedIndexes == null) checkedIndexes = [];
        }

        if (selectedIndex == null && checkedIndexes == null) return;

        var old_selectedIndex = instance.getSelectedIndex();
        var old_checkedIndexes = instance.getCheckedIndexes();

        if (selectedIndex != null && selectedIndex != old_selectedIndex) {
            $container.children().removeClass(checkedClass).removeClass(selectedClass);
            $container.children().find('[data-rowHeader] input[type=checkbox]').prop('checked', false);

            if (selectedIndex > -1) {
                $container.children(':eq(' + selectedIndex + ')').addClass(selectedClass);
                $container.children(':eq(' + selectedIndex + ')').find('[data-rowHeader] input[type=checkbox]').prop('checked', true);
            }
        }

        if (checkedIndexes != null) {
            checkedIndexes.sort(function (a, b) {
                return a - b;
            });

            var checkedStr = checkedIndexes.toString();
            if (checkedStr != old_checkedIndexes.toString()) {
                $container.children().find('[data-rowHeader] input[type=checkbox]').each(function (index, element) {
                    if ($(element).prop('checked')) {
                        var idx = checkedStr.indexOf(index);
                        if (idx == -1) {
                            $(element).prop('checked', false);
                            $(element).parent().parent().removeClass(checkedClass).removeClass(selectedClass);
                        }
                    }
                    else {
                        var idx = checkedStr.indexOf(index);
                        if (idx != -1) {
                            $(element).prop('checked', true);
                            $(element).parent().parent().addClass(checkedClass);
                        }
                    }
                });
            }
        }

        if (ignoreOnChanged) return;

        onSelectedChanged(old_selectedIndex);
        onCheckedChanged(old_checkedIndexes);
    };

    DataBox.prototype.getSelectedIndex = function () {
        var selectedClass = this.options.selectedClass;
        var selected = this.$container.children('.' + selectedClass);
        return selected.index();
    };

    DataBox.prototype.getCheckedIndexes = function () {
        var indexes = [];
        var checkedClass = this.options.checkedClass;
        this.$container.children().each(function (index, element) {
            var checked = $(element).find('[data-rowHeader] input[type=checkbox]').prop('checked');
            if (checked) {
                indexes.push($(element).index());
            }
        });
        return indexes;
    };

    DataBox.prototype.getData = function () {
        var data = [];
        this.$container.children().each(function (index, element) {
            data.push(JSON.parse($(this).attr('data-data')));
        });
        return data;
    };

    DataBox.prototype.checkAll = function () {
        var $container = this.$container;
        var checkedClass = this.options.checkedClass;
        var checkedChanged = this.options.checkedChanged;

        var checkedAll = true;
        $container.children().find('[data-rowHeader] input[type=checkbox]').each(function (index, element) {
            if (!$(element).prop('checked')) {
                checkedAll = false;
                return false;
            }
        });

        if (!checkedAll) {
            $container.children().find('[data-rowHeader] input[type=checkbox]').each(function (index, element) {
                if (!$(element).prop('checked')) {
                    $(element).prop('checked', true);
                    $(element).parent().parent().addClass(checkedClass);
                }
            });

            if (checkedChanged != null) {
                var len = $container.children().length;
                var indexes = [];
                for (var i = 0; i < len; i++) {
                    indexes.push(i);
                }
                checkedChanged(indexes);
            }
        }
    };

    DataBox.prototype.uncheckAll = function () {
        var $container = this.$container;
        var selectedClass = this.options.selectedClass;
        var checkedClass = this.options.checkedClass;
        var selectedChanged = this.options.selectedChanged;
        var checkedChanged = this.options.checkedChanged;

        var hasChecked = false;
        var selectedIndex = this.getSelectedIndex();
        if (selectedIndex > -1) {
            hasChecked = true;
        }
        else {
            $container.children().find('[data-rowHeader] input[type=checkbox]').each(function (index, element) {
                if ($(element).prop('checked')) {
                    hasChecked = true;
                    return false;
                }
            });
        }

        if (hasChecked) {
            $container.children().find('[data-rowHeader] input[type=checkbox]').prop('checked', false);
            $container.children().removeClass(checkedClass).removeClass(selectedClass);
            if (selectedIndex > -1) {
                if (selectedChanged != null) {
                    selectedChanged(-1);
                }
            }
            if (checkedChanged != null) {
                checkedChanged([]);
            }
        }
    };

    DataBox.prototype.append = function (data) {
        var selectedIndex = this.getSelectedIndex();
        var checkedIndexes = this.getCheckedIndexes();
        var newData = this.getData().concat(data);

        this.setting({
            "data": newData,
            "selectedIndex": selectedIndex,
            "checkedIndexes": checkedIndexes
        },
        true);
    };

    DataBox.prototype.insert = function (arg) {
        var index = arg.index;
        var data = [];
        if ($.isArray(arg.data)) {
            data = arg.data.concat();
        }
        else {
            data.push(arg.data);
        }

        var selectedIndex = this.getSelectedIndex();
        var checkedIndexes = this.getCheckedIndexes();
        var checkedStr = checkedIndexes.toString();
        var thisData = this.getData();

        var newData = [];
        var newSelected = -1;
        var newChecked = [];
        for (var i = 0; i < index; i++) {
            newData.push(thisData[i]);
            if (i == selectedIndex) newSelected = i;
            if (checkedStr.indexOf(i) != -1) newChecked.push(i);
        }

        newData = newData.concat(data);

        var dataLen = data.length;
        for (var i = index; i < thisData.length; i++) {
            newData.push(thisData[i]);
            if (i == selectedIndex) newSelected = i + dataLen;
            if (checkedStr.indexOf(i) != -1) newChecked.push(i + dataLen);
        }

        this.setting(
        {
            "data": newData,
            "selectedIndex": newSelected,
            "checkedIndexes": newChecked
        },
        true);

        var selectedChanged = this.options.selectedChanged;
        var checkedChanged = this.options.checkedChanged;

        if (newSelected != selectedIndex) {
            if (selectedChanged != null) {
                selectedChanged(newSelected);
            }
        }
        if (newChecked.toString() != checkedStr) {
            if (checkedChanged != null) {
                checkedChanged(newChecked);
            }
        }
    };

    DataBox.prototype.delete = function (index) {
        var indexes = [];
        if ($.isArray(index)) {
            indexes = index.concat();
            indexes.sort(function (a, b) { return a - b; });
        }
        else {
            indexes.push(index);
        }
        var delStr = indexes.toString();

        var selectedIndex = this.getSelectedIndex();
        var checkedIndexes = this.getCheckedIndexes();
        var checkedStr = checkedIndexes.toString();
        var data = this.getData();

        var newData = [];
        var newSelected = -1;
        var newChecked = [];
        for (var i = 0; i < data.length; i++) {
            if (delStr.indexOf(i) == -1) {
                newData.push(data[i]);
                var newLen = newData.length - 1;
                if (i == selectedIndex) newSelected = newLen
                if (checkedStr.indexOf(i) != -1) newChecked.push(newLen);
            }
        }

        this.setting(
            {
                "data": newData,
                "selectedIndex": newSelected,
                "checkedIndexes": newChecked
            },
            true);

        var selectedChanged = this.options.selectedChanged;
        var checkedChanged = this.options.checkedChanged;

        if (newSelected != selectedIndex) {
            if (selectedChanged != null) {
                selectedChanged(newSelected);
            }
        }
        if (newChecked.toString() != checkedStr) {
            if (checkedChanged != null) {
                checkedChanged(newChecked);
            }
        }
    };

    DataBox.prototype.update = function (arg) {
        var items = [];
        if ($.isArray(arg)) {
            items = arg.concat();
        }
        else {
            items.push(arg);
        }

        var selectedIndex = this.getSelectedIndex();
        var checkedIndexes = this.getCheckedIndexes();
        var checkedStr = checkedIndexes.toString();
        var newData = this.getData();

        var selectedIsChanged = false;
        var checkedIsChanged = false;
        for (var i = 0; i < items.length; i++) {
            var index = items[i].index;
            var data = items[i].data;

            newData[index] = data;

            if (index == selectedIndex) {
                selectedIsChanged = true;
            }
            if (checkedStr.indexOf(index) != -1) {
                checkedIsChanged = true;
            }
        }

        this.setting(
        {
            "data": newData,
            "selectedIndex": selectedIndex,
            "checkedIndexes": checkedIndexes
        },
        true);

        var selectedChanged = this.options.selectedChanged;
        var checkedChanged = this.options.checkedChanged;

        if (selectedIsChanged) {
            if (selectedChanged != null) {
                selectedChanged(selectedIndex);
            }
        }
        if (checkedIsChanged) {
            if (checkedChanged != null) {
                checkedChanged(checkedIndexes);
            }
        }
    };

    // Plugin 
    function Plugin(options, arg) {
        if (typeof options == 'string') {
            var $this = $(this);
            var data = $this.data('xd.dataBox');
            if (options == 'getSelectedIndex') {
                if (!data) return -1;
                return data.getSelectedIndex();
            }
            else if (options == 'getCheckedIndexes') {
                if (!data) return [];
                return data.getCheckedIndexes();
            }
            else if (options == 'getData') {
                if (!data) return [];
                return data.getData();
            }
        }
        return this.each(function () {
            var $this = $(this);
            var data = $this.data('xd.dataBox');
            if (!data) $this.data('xd.dataBox', (data = new DataBox(this)));
            if (typeof options == 'object') {
                data.setting(options);
            }
            if (typeof options == 'string') {
                if (options == 'checkAll') {
                    data.checkAll();
                }
                else if (options == 'uncheckAll') {
                    data.uncheckAll();
                }
                else if (options == 'append') {
                    data.append(arg);
                }
                else if (options == 'insert') {
                    data.insert(arg);
                }
                else if (options == 'delete') {
                    data.delete(arg);
                }
                else if (options == 'update') {
                    data.update(arg);
                }
            }
        })
    }

    var old = $.fn.dataBox;

    $.fn.dataBox = Plugin;
    $.fn.dataBox.Constructor = DataBox;

    // No conflict
    $.fn.dataBox.noConflict = function () {
        $.fn.dataBox = old;
        return this;
    };

}(jQuery);